/**
  ******************************************************************************
  * @file    can_bootloader.c
  * $Author: wdluo $
  * $Revision: 17 $
  * $Date:: 2012-07-06 11:16:48 +0800 #$
  * @brief   基于CAN总线的Bootloader程序.
  ******************************************************************************
  * @attention
  *
  *<h3><center>&copy; Copyright 2009-2012, ViewTool</center>
  *<center><a href="http:\\www.viewtool.com">http://www.viewtool.com</a></center>
  *<center>All Rights Reserved</center></h3>
  * 
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "can_boot.h"
#include "flash_if.h"
#if ENCRYPT
#include "aes.h"
#endif
/* Private typedef -----------------------------------------------------------*/
typedef  void (*pFunction)(void);
pFunction JumpToApplication;
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/


/**
 * @brief  获取固件升级标志
 * @return 固件升级标志状态
 * @retval 1 固件升级标志有效
 * @retval 0 固件升级标志无效
 */
uint8_t CAN_BOOT_GetProgRequest(void)
{
  //读取请求固件升级标志，需要自己实现
  if(*((uint32_t *)BOOT_REQ_FLAG_ADDR)==BOOT_REQ_FLAG){
    return 1;
  }else{
    return 0;
  }
}
/**
 * @brief  将固件升级标志置位
 */
void CAN_BOOT_SetProgRequest(void)
{
  uint32_t data = BOOT_REQ_FLAG;
  __set_PRIMASK(1);//关闭中断  
  FLASH_If_Erase(APP_VALID_FLAG_ADDR,0x20000);
  FLASH_If_Write(BOOT_REQ_FLAG_ADDR,&data,1);
  __set_PRIMASK(0);//开启中断
}

/**
 * @brief  将固件升级标志复位
 */
void CAN_BOOT_ResetProgRequest(void)
{
  uint32_t AppValidFlag = *((uint32_t *)APP_VALID_FLAG_ADDR);
  __set_PRIMASK(1);//关闭中断
  FLASH_If_Erase(APP_VALID_FLAG_ADDR,0x20000);
  FLASH_If_Write(APP_VALID_FLAG_ADDR,&AppValidFlag,1);
  __set_PRIMASK(0);//开启中断
}

/**
 * @brief  将固件升级标志置位
 */
void CAN_BOOT_SetAppValid(void)
{
  uint32_t data = APP_VALID_FLAG;
  __set_PRIMASK(1);//关闭中断
  FLASH_If_Write(APP_VALID_FLAG_ADDR,&data,1);
  __set_PRIMASK(0);//开启中断
}

/**
 * @brief 软件复位程序 
 */
void CAN_BOOT_Reset(void)
{
  __set_PRIMASK(1);//关闭中断
  NVIC_SystemReset();
}
/**
 * @brief 执行APP程序 
 */
void CAN_BOOT_ExeApp(void)
{
  CAN_BOOT_JumpToApplication(APP_START_ADDR);
}
/**
 * @brief 擦除应用程序
 * @return 擦除状态
 * @retval 0 成功
 * @retval 1 失败
 */
uint8_t CAN_BOOT_EraseApp(void)
{
  return CAN_BOOT_ErasePage();
}

/**
 * @brief 判断APP应用程序是否有效
 * @return 应用程序有效状态
 * @retval 0 无效
 * @retval 1 有效
 */
uint8_t CAN_BOOT_CheckApp(void)
{
  return 1;
}

/**
 * @brief  将数据写入Flash
 * @param  Addr 数据起始地址
 * @param  pData 数据指针
 * @param  DataLen 写入数据字节数
 * @return 写入状态
 * @retval 0 成功
 * @retval 1 失败
 */
uint8_t CAN_BOOT_WriteDataToFlash(uint32_t Addr,uint8_t *pData,uint32_t DataLen)
{
  return CAN_BOOT_ProgramDatatoFlash(Addr,pData,DataLen);
}

/**
  * @brief  将数据烧写到指定地址的Flash中 。
  * @param  Address Flash起始地址。
  * @param  Data 数据存储区起始地址。
  * @param  DataNum 数据字节数。
  * @retval 数据烧写状态。
  */
uint8_t CAN_BOOT_ProgramDatatoFlash(uint32_t StartAddr,uint8_t *pData,uint32_t DataNum) 
{
  uint32_t FLASHStatus;
  __set_PRIMASK(1);//关闭中断
  FLASHStatus = FLASH_If_Write(StartAddr,(uint32_t *)pData,DataNum/4);
  __set_PRIMASK(0);//开启中断
  if(FLASHStatus == FLASHIF_OK){
    return 0;
  }else{
    return 1;
  }
}
/**
  * @brief  擦出指定扇区区间的Flash数据 。
  * @param  StartPage 起始扇区地址
  * @param  EndPage 结束扇区地址
  * @retval 扇区擦出状态  
  */
uint8_t CAN_BOOT_ErasePage(void)
{
  uint32_t FLASHStatus;
  __disable_irq();
  FLASHStatus = FLASH_If_Erase(APP_START_ADDR,0x80000);
   __enable_irq();
  if(FLASHStatus == FLASHIF_OK){
    return 0;
  }else{
    return 1;
  }
}

/**
 * @brief  执行指定地址处程序
 * @param  Addr 程序起始地址
 */
void CAN_BOOT_JumpToApplication(uint32_t Addr)
{
  pFunction JumpToApp;
  __IO uint32_t JumpAddress; 
  __set_PRIMASK(1);//关闭全局中断，注意，必须在APP中打开全局中断，否则可能会导致APP中断程序不正常
  if (((*(__IO uint32_t*)Addr) & 0x20FE0000 ) == 0x20000000)
  { 
    /* Jump to user application */
    JumpAddress = *(__IO uint32_t*) (Addr + 4);
    JumpToApplication = (pFunction) JumpAddress;
    /* Initialize user application's Stack Pointer */
    __set_MSP(*(__IO uint32_t*) Addr);
    if(Addr == BOOT_START_ADDR){
      printf("[%s]reset system\n",(FW_TYPE==FW_TYPE_BOOT)?"BOOT":"APP");
      HAL_DeInit();//必须调用，否则可能无法正常跳转程序
      NVIC_SystemReset();
    }else{
      printf("[%s]jump to app addr = 0x%08X\n",(FW_TYPE==FW_TYPE_BOOT)?"BOOT":"APP",Addr);
      HAL_DeInit();//必须调用，否则可能无法正常跳转程序
      JumpToApplication();
    }
  }
}



